#include "fxcc/core/graph/opengl3/ImGuiGlfwApp.h"
#include "fxcc/core/graph/common/Geometry.h"
#include "fxcc/core/graph/common/BoneNode.h"

#include "fxcc/core/graph/common/Scene.h"
#include "fxcc/imgui/Editor.h"

using namespace fxcc::platform;
using namespace fxcc::graph;
using namespace fxcc::graph::opengl3;
using namespace fxcc::imgui::graph::opengl3;
using BaseApp = fxcc::imgui::graph::opengl3::GlfwApp;

#undef main

struct SimpleApp : public BaseApp
{
public:
    fxcc::graph::common::Transform transform;

    std::shared_ptr<fxcc::graph::opengl3::RenderTargetJack> m_RenderTarget;

    std::shared_ptr<fxcc::graph::opengl3::MeshJack> m_CubeMesh;

    std::shared_ptr<fxcc::graph::opengl3::Effect3d> m_Effect;

    glm::vec4 color;

    int m_TessLevelInner{ 3 };

    int m_TessLevelOuter0{ 3 };

    int m_TessLevelOuter1{ 3 };

    int m_TessLevelOuter2{ 3 };

    fxcc::graph::common::Camera camera;

    SimpleApp(const fxcc::platform::common::App::Desc &desc) : BaseApp(desc), color(1.0)
    {
    }
    bool Init() override
    {
        if (!BaseApp::Init())
        {
            return false;
        }

        if (!InitResources())
        {
            return false;
        }

        return true;
    };

    fxcc::graph::common::Effect3d::BoneData m_BoneData;

    void printOpenGLESVersion()
    {
        const char *version = (const char *)glGetString(GL_VERSION);
        std::cout << "OpenGL ES Version: " << version << std::endl;
    };

    bool InitResources()
    {
        transform.m_Scale = glm::vec3(1.f);

        camera.SetPosition(glm::vec3(5.0f));
        camera.LookAt(glm::vec3(0.0f));
        camera.nNear = 0.01f;
        camera.nFar= 2500.f;

        m_CubeMesh = std::make_shared<fxcc::graph::opengl3::MeshJack>(fxcc::graph::common::Geometry::CreateQuad());

        ShaderDesc shaderDesc;
        shaderDesc.m_FragCode = R"(

out  vec4 FragColor;

void main()
{
    FragColor=vec4(0.0f,1.0f,0.2f,1.0f);
}
)";
        shaderDesc.m_TessellationControlCode = R"(


in vec4 FragPosAlpha[]; 
out vec4 tcPosition[];  


uniform float m_TessLevelInner;
uniform float m_TessLevelOuter0;
uniform float m_TessLevelOuter1;
uniform float m_TessLevelOuter2;

layout(vertices = 3) out;

void main() {
    
    tcPosition[gl_InvocationID] = FragPosAlpha[gl_InvocationID];
    gl_out[gl_InvocationID].gl_Position = FragPosAlpha[gl_InvocationID];

    if (gl_InvocationID == 0) {
        gl_TessLevelInner[0] = m_TessLevelInner;
        gl_TessLevelOuter[0] = m_TessLevelOuter0;
        gl_TessLevelOuter[1] = m_TessLevelOuter1;
        gl_TessLevelOuter[2] = m_TessLevelOuter2;
    }
}

)";

        shaderDesc.m_TessellationEvalutionCode = R"(

layout(triangles, fractional_odd_spacing) in;

in vec4 tcPosition[];  

void main() {

    vec3 p0 = gl_in[0].gl_Position.xyz;
    vec3 p1 = gl_in[1].gl_Position.xyz;
    vec3 p2 = gl_in[2].gl_Position.xyz;

    float u = gl_TessCoord.x;
    float v = gl_TessCoord.y;
    float w = gl_TessCoord.z;

    vec3 pos = u * p0 + v * p1 + w * p2;
    vec4 fragPos = vec4(pos, 1.0);

    gl_Position = proj* view* fragPos;

}
)";

        m_Effect = std::make_shared<fxcc::graph::opengl3::Effect3d>(shaderDesc);

        m_Effect->m_FixedShaderDesc.OutDir("debug");

        m_Effect->m_PipeLineType.m_RasterizerType = fxcc::graph::common::RasterizerType::_NONE_;
        return true;
    };

    fxcc::imgui::Editor editor;

    void OnEdit() override
    {
        ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());

        ImGui::Begin("RT");
        editor.EditInst<fxcc::graph::common::RenderTargetJack>("rt", *m_RenderTarget);
        ImGui::End();

        ImGui::Begin("attachment0");
        editor.AddFrameBuffer(m_RenderTarget->m_Textures[0]->GetTextureId());
        ImGui::End();

  
    
        ImGui::Begin("tess");

        editor.EditInst<int>("m_TessLevelInner", m_TessLevelInner);
        editor.EditInst<int>("m_TessLevelOuter0", m_TessLevelOuter0);
        editor.EditInst<int>("m_TessLevelOuter1", m_TessLevelOuter1);
        editor.EditInst<int>("m_TessLevelOuter2", m_TessLevelOuter2);
        ImGui::End();
    }
    void OnDestroy() override
    {
      
        BaseApp::OnDestroy();
    }
    void OnFrameRender() override
    {
        GLenum error = glGetError();

        m_RenderTarget->Begin();

        m_Effect->Begin();
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        //m_Effect->ClearBgColor(color);
        m_Effect->ClearBuffer(1, 1);
        Effect3d::PassData passData(camera);
        passData.m_UseTessllation = true;
        m_Effect->SetCamera(camera);

        m_Effect->m_Shader->Use();
        m_Effect->m_Shader->setFloat("m_TessLevelInner", m_TessLevelInner);
        m_Effect->m_Shader->setFloat("m_TessLevelOuter0", m_TessLevelOuter0);
        m_Effect->m_Shader->setFloat("m_TessLevelOuter1", m_TessLevelOuter1);
        m_Effect->m_Shader->setFloat("m_TessLevelOuter2", m_TessLevelOuter2);
        Effect3d::ObjData objData(transform);
        m_Effect->m_ObjBuffer->Upload<Effect3d::ObjData>(objData);

        m_CubeMesh->Bind();
        glPatchParameteri(GL_PATCH_VERTICES, 3);
        m_CubeMesh->DrawElements(GL_PATCHES, m_CubeMesh->m_NumIndices, GL_UNSIGNED_INT, 0);
   
        m_Effect->End();
        m_RenderTarget->End();
    };


    void OnFramebuffersize(int w, int h) override
    {
        opengl3::GlfwApp::OnFramebuffersize(w, h);
        camera.SetAspect(m_Desc.Aspect());
        
        fxcc::graph::common::RenderTargetJack::Desc desc(1, w, h, 4, 0);
        m_RenderTarget = std::make_shared<fxcc::graph::opengl3::RenderTargetJack>(desc);

    }
    void OnUpdate() override
    {
        opengl3::GlfwApp::OnUpdate();

        float ctTime = m_GlfwlTimer.m_CurrentTime;
        camera.FreeMove(m_Input, m_GlfwlTimer.m_DeltaTime, { 50.0f,50.0f });

        float deltaTime = m_GlfwlTimer.m_DeltaTime;


    }
};

int main()
{
    ztclog::pushFunction([](const ztclog::Line &line)
                         {
		if (line.m_Type != ztclog::Line::Type::_line_debug_)
		{
			std::cout << line.m_Text << std::endl;
		} });

    using _App = SimpleApp;
    using _Desc = fxcc::platform::common::App::Desc;

    _Desc desc;
    desc.m_Pos = glm::ivec2(100, 100);
    desc.m_Size = glm::ivec2(900, 600);
    desc.m_ScaleFactor = 1.0f;

    desc.m_Title = "test font";
    std::shared_ptr<_App> _app = std::make_shared<_App>(desc);
    return _app->Run();
};
